package com.typhoon.rpc.server;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.typhoon.rpc.common.codec.RpcDecoder;
import com.typhoon.rpc.common.codec.RpcEncoder;
import com.typhoon.rpc.common.dto.RpcRequest;
import com.typhoon.rpc.common.dto.RpcResponse;
import com.typhoon.rpc.common.util.StringUtil;
import com.typhoon.rpc.registry.ServiceRegistry;
import com.typhoon.rpc.server.annotations.RpcService;
import com.typhoon.rpc.server.handler.RpcServerHandler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class RpcServer implements ApplicationContextAware, InitializingBean   {
	 private static final Logger LOGGER = LoggerFactory.getLogger(RpcServer.class);

	    /**
	     * 服务地址
	     */
	    private String serviceAddress;

	    /**
	     * 服务注册接口
	     */
	    private ServiceRegistry serviceRegistry;

	    /**
	     * 存放 服务名 与 服务对象 之间的映射关系
	     */
	    private Map<String, Object> handlerMap = new HashMap<>();

	    public RpcServer(String serviceAddress) {
	        this.serviceAddress = serviceAddress;
	    }

	    public RpcServer(String serviceAddress, ServiceRegistry serviceRegistry) {
	        this.serviceAddress = serviceAddress;
	        this.serviceRegistry = serviceRegistry;
	    }

	    @Override
	    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
	        // 扫描带有 RpcService 注解的类并初始化 handlerMap 对象
	        Map<String, Object> serviceBeanMap = ctx.getBeansWithAnnotation(RpcService.class);
	        if (MapUtils.isNotEmpty(serviceBeanMap)) {//存在使用RpcService注解的类
	            for (Object serviceBean : serviceBeanMap.values()) {
	                RpcService rpcService = serviceBean.getClass().getAnnotation(RpcService.class);//获取每个类上的注解
	                String serviceName = rpcService.value().getName();//服务名
	                handlerMap.put(serviceName, serviceBean);//将服务名,服务对应的实例放入map
	            }
	        }
	    }

	    @Override
	    public void afterPropertiesSet() throws Exception {//bean初始化完成后触发此操作。初始化完成后启动服务
	        EventLoopGroup bossGroup = new NioEventLoopGroup();//接收请求线程
	        EventLoopGroup workerGroup = new NioEventLoopGroup();//工作线程
	        try {
	            // 创建并初始化 Netty 服务端 Bootstrap 对象
	            ServerBootstrap bootstrap = new ServerBootstrap();
	            bootstrap.group(bossGroup, workerGroup);
	            bootstrap.channel(NioServerSocketChannel.class);
	            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
	                @Override
	                public void initChannel(SocketChannel channel) throws Exception {
	                    ChannelPipeline pipeline = channel.pipeline();
	                    pipeline.addLast(new RpcDecoder(RpcRequest.class)); // 解码 RPC 请求
	                    pipeline.addLast(new RpcEncoder(RpcResponse.class)); // 编码 RPC 响应
	                    pipeline.addLast(new RpcServerHandler(handlerMap)); // 处理 RPC 请求
	                }
	            });
	            bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
	            bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
	            // 获取 RPC 服务器的 IP 地址与端口号
	            String[] addressArray = StringUtil.split(serviceAddress, ":");
	            String ip = addressArray[0];
	            int port = Integer.parseInt(addressArray[1]);
	            // 启动 RPC 服务器
	            ChannelFuture future = bootstrap.bind(ip, port).sync();
	            // 注册 RPC 服务地址
	            if (serviceRegistry != null) {
	                for (String interfaceName : handlerMap.keySet()) {
	                    serviceRegistry.register(interfaceName, serviceAddress);
	                    LOGGER.debug("register service: {} => {}", interfaceName, serviceAddress);
	                }
	            }
	            LOGGER.debug("server started on port {}", port);
	            // 关闭 RPC 服务器
	            future.channel().closeFuture().sync();
	        } finally {
	            workerGroup.shutdownGracefully();
	            bossGroup.shutdownGracefully();
	        }
	    }
}
